<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MQTT通信客户端</title>
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: #333;
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        header {
            text-align: center;
            margin-bottom: 30px;
            color: white;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }
        
        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }
        
        .description {
            font-size: 1.1rem;
            opacity: 0.9;
            max-width: 800px;
            margin: 0 auto 20px;
        }
        
        .dashboard {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-bottom: 30px;
        }
        
        @media (max-width: 768px) {
            .dashboard {
                grid-template-columns: 1fr;
            }
        }
        
        .panel {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            transition: transform 0.3s ease;
        }
        
        .panel:hover {
            transform: translateY(-5px);
        }
        
        .panel h2 {
            color: #1a2a6c;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid #fdbb2d;
        }
        
        .form-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
            color: #444;
        }
        
        input, textarea, select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 1rem;
            transition: border 0.3s;
        }
        
        input:focus, textarea:focus, select:focus {
            border-color: #1a2a6c;
            outline: none;
        }
        
        button {
            background: #1a2a6c;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 1rem;
            transition: background 0.3s;
            margin-right: 10px;
            margin-bottom: 10px;
        }
        
        button:hover {
            background: #2a3a9c;
        }
        
        button.success {
            background: #28a745;
        }
        
        button.success:hover {
            background: #218838;
        }
        
        button.danger {
            background: #dc3545;
        }
        
        button.danger:hover {
            background: #c82333;
        }
        
        button.warning {
            background: #fdbb2d;
            color: #333;
        }
        
        button.warning:hover {
            background: #e6a723;
        }
        
        .status-indicator {
            display: inline-block;
            width: 12px;
            height: 12px;
            border-radius: 50%;
            margin-right: 8px;
        }
        
        .status-connected {
            background: #28a745;
            box-shadow: 0 0 8px #28a745;
        }
        
        .status-disconnected {
            background: #dc3545;
        }
        
        .status-connecting {
            background: #fdbb2d;
            animation: pulse 1.5s infinite;
        }
        
        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }
        
        .message-container {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 10px;
            background: #f9f9f9;
            margin-top: 10px;
        }
        
        .message {
            padding: 8px;
            margin-bottom: 8px;
            border-radius: 5px;
            background: white;
            border-left: 4px solid #1a2a6c;
        }
        
        .message.received {
            border-left-color: #28a745;
        }
        
        .message.sent {
            border-left-color: #fdbb2d;
        }
        
        .message.system {
            border-left-color: #6c757d;
            background: #e9ecef;
        }
        
        .message-header {
            font-weight: bold;
            margin-bottom: 5px;
            display: flex;
            justify-content: space-between;
        }
        
        .message-topic {
            color: #6c757d;
            font-size: 0.9rem;
        }
        
        .message-content {
            word-break: break-word;
        }
        
        .timestamp {
            font-size: 0.8rem;
            color: #888;
        }
        
        .connection-status {
            display: flex;
            align-items: center;
            margin-bottom: 15px;
            padding: 10px;
            border-radius: 5px;
            background: #f8f9fa;
        }
        
        .status-text {
            font-weight: bold;
        }
        
        .topic-list {
            margin-top: 15px;
        }
        
        .topic-item {
            display: flex;
            justify-content: space-between;
            padding: 8px;
            background: #f1f3f4;
            margin-bottom: 5px;
            border-radius: 5px;
        }
        
        .topic-actions button {
            padding: 5px 10px;
            font-size: 0.8rem;
            margin-right: 0;
            margin-left: 5px;
        }
        
        .server-options {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 10px;
            margin-top: 10px;
        }
        
        .server-option {
            padding: 10px;
            background: #f1f3f4;
            border-radius: 5px;
            cursor: pointer;
            transition: background 0.3s;
            text-align: center;
        }
        
        .server-option:hover {
            background: #e2e6ea;
        }
        
        .server-option.active {
            background: #1a2a6c;
            color: white;
        }
        
        footer {
            text-align: center;
            color: white;
            margin-top: 30px;
            opacity: 0.8;
        }
        
        .info-box {
            background: #d1ecf1;
            border-left: 4px solid #0c5460;
            padding: 10px 15px;
            margin-bottom: 15px;
            border-radius: 0 5px 5px 0;
        }
        
        .info-box h3 {
            color: #0c5460;
            margin-bottom: 5px;
        }
        
        .info-box p {
            margin-bottom: 5px;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>MQTT通信客户端</h1>
            <p class="description">连接MQTT服务器，订阅主题并发送/接收消息。支持WebSocket连接。</p>
        </header>
        
        <div class="dashboard">
            <div class="panel">
                <h2>连接配置</h2>
                <div class="connection-status">
                    <span class="status-indicator status-disconnected" id="statusIndicator"></span>
                    <span class="status-text" id="statusText">未连接</span>
                </div>
                
                <div class="info-box">
                    <h3>连接提示</h3>
                    <p>如果连接失败，请尝试不同的服务器或检查网络设置。</p>
                    <p>推荐使用公共测试服务器：test.mosquitto.org</p>
                </div>
                
                <div class="form-group">
                    <label for="brokerUrl">MQTT服务器地址</label>
                    <input type="text" id="brokerUrl" placeholder="例如：wss://test.mosquitto.org:8081" value="wss://test.mosquitto.org:8081">
                </div>
                
                <div class="form-group">
                    <label>推荐服务器</label>
                    <div class="server-options">
                        <div class="server-option active" data-url="wss://test.mosquitto.org:8081">Mosquitto (WebSocket)</div>
                        <div class="server-option" data-url="ws://broker.emqx.io:8083/mqtt">EMQX (WebSocket)</div>
                        <div class="server-option" data-url="wss://mqtt.eclipseprojects.io:443/mqtt">Eclipse (WebSocket)</div>
                    </div>
                </div>
                
                <div class="form-group">
                    <label for="clientId">客户端ID</label>
                    <input type="text" id="clientId" placeholder="留空将自动生成">
                </div>
                
                <div class="form-group">
                    <label for="username">用户名</label>
                    <input type="text" id="username" placeholder="可选">
                </div>
                
                <div class="form-group">
                    <label for="password">密码</label>
                    <input type="password" id="password" placeholder="可选">
                </div>
                
                <div class="form-group">
                    <button id="connectBtn" class="success">连接</button>
                    <button id="disconnectBtn" class="danger" disabled>断开连接</button>
                    <button id="testBtn" class="warning">测试连接</button>
                </div>
            </div>
            
            <div class="panel">
                <h2>订阅主题</h2>
                <div class="form-group">
                    <label for="subscribeTopic">主题</label>
                    <input type="text" id="subscribeTopic" placeholder="例如：test/topic" value="">
                </div>
                
                <div class="form-group">
                    <button id="subscribeBtn" class="warning" disabled>订阅</button>
                </div>
                
                <div class="topic-list" id="topicList">
                    <p>已订阅的主题将显示在这里</p>
                </div>
            </div>
            
            <div class="panel">
                <h2>发布消息</h2>
                <div class="form-group">
                    <label for="publishTopic">主题</label>
                    <input type="text" id="publishTopic" placeholder="例如：test/topic" value="">
                </div>
                
                <div class="form-group">
                    <label for="message">消息内容</label>
                    <textarea id="message" rows="4" placeholder="输入要发送的消息"></textarea>
                </div>
                
                <div class="form-group">
                    <button id="publishBtn" class="success" disabled>发布</button>
                    <button id="autoPublishBtn" class="warning" disabled>自动发布</button>
                </div>
            </div>
            
            <div class="panel">
                <h2>消息日志</h2>
                <div class="message-container" id="messageContainer">
                    <div class="message system">
                        <div class="message-header">系统消息</div>
                        <div class="message-content">等待连接...</div>
                    </div>
                </div>
                <div class="form-group" style="margin-top: 10px;">
                    <button id="clearBtn" class="danger">清空日志</button>
                    <span id="messageCount">消息: 0</span>
                </div>
            </div>
        </div>
        
        <footer>
            <p>MQTT通信客户端 &copy; 2025 | 使用MQTT.js库| puzechuan版权所有</p>
        </footer>
    </div>

    <script>
        // MQTT客户端实例
        let client = null;
        let isConnected = false;
        let autoPublishInterval = null;
        const subscribedTopics = new Set();
        let messageCount = 0;
        
        // DOM元素
        const brokerUrlInput = document.getElementById('brokerUrl');
        const clientIdInput = document.getElementById('clientId');
        const usernameInput = document.getElementById('username');
        const passwordInput = document.getElementById('password');
        const connectBtn = document.getElementById('connectBtn');
        const disconnectBtn = document.getElementById('disconnectBtn');
        const testBtn = document.getElementById('testBtn');
        const subscribeTopicInput = document.getElementById('subscribeTopic');
        const subscribeBtn = document.getElementById('subscribeBtn');
        const publishTopicInput = document.getElementById('publishTopic');
        const messageInput = document.getElementById('message');
        const publishBtn = document.getElementById('publishBtn');
        const autoPublishBtn = document.getElementById('autoPublishBtn');
        const messageContainer = document.getElementById('messageContainer');
        const topicList = document.getElementById('topicList');
        const statusIndicator = document.getElementById('statusIndicator');
        const statusText = document.getElementById('statusText');
        const clearBtn = document.getElementById('clearBtn');
        const messageCountElement = document.getElementById('messageCount');
        const serverOptions = document.querySelectorAll('.server-option');
        
        // 生成随机客户端ID
        function generateClientId() {
            return 'mqtt_client_' + Math.random().toString(16).substr(2, 8);
        }
        
        // 更新连接状态
        function updateConnectionStatus(connected) {
            isConnected = connected;
            
            if (connected) {
                statusIndicator.className = 'status-indicator status-connected';
                statusText.textContent = '已连接';
                connectBtn.disabled = true;
                disconnectBtn.disabled = false;
                subscribeBtn.disabled = false;
                publishBtn.disabled = false;
                autoPublishBtn.disabled = false;
            } else {
                statusIndicator.className = 'status-indicator status-disconnected';
                statusText.textContent = '未连接';
                connectBtn.disabled = false;
                disconnectBtn.disabled = true;
                subscribeBtn.disabled = true;
                publishBtn.disabled = true;
                autoPublishBtn.disabled = true;
                subscribedTopics.clear();
                updateTopicList();
                
                // 停止自动发布
                if (autoPublishInterval) {
                    clearInterval(autoPublishInterval);
                    autoPublishInterval = null;
                    autoPublishBtn.textContent = '自动发布';
                }
            }
        }
        
        // 添加消息到日志
        function addMessage(topic, message, type) {
            const messageElement = document.createElement('div');
            messageElement.className = `message ${type}`;
            
            const now = new Date();
            const timestamp = now.toLocaleTimeString();
            
            messageElement.innerHTML = `
                <div class="message-header">
                    <span>${type === 'sent' ? '发送' : '接收'}的消息</span>
                    <span class="timestamp">${timestamp}</span>
                </div>
                <div class="message-topic">主题: ${topic}</div>
                <div class="message-content">${message}</div>
            `;
            
            messageContainer.appendChild(messageElement);
            messageContainer.scrollTop = messageContainer.scrollHeight;
            
            // 更新消息计数
            messageCount++;
            messageCountElement.textContent = `消息: ${messageCount}`;
        }
        
        // 添加系统消息
        function addSystemMessage(message) {
            const messageElement = document.createElement('div');
            messageElement.className = 'message system';
            
            const now = new Date();
            const timestamp = now.toLocaleTimeString();
            
            messageElement.innerHTML = `
                <div class="message-header">
                    <span>系统消息</span>
                    <span class="timestamp">${timestamp}</span>
                </div>
                <div class="message-content">${message}</div>
            `;
            
            messageContainer.appendChild(messageElement);
            messageContainer.scrollTop = messageContainer.scrollHeight;
            
            // 更新消息计数
            messageCount++;
            messageCountElement.textContent = `消息: ${messageCount}`;
        }
        
        // 更新已订阅主题列表
        function updateTopicList() {
            topicList.innerHTML = '';
            
            if (subscribedTopics.size === 0) {
                topicList.innerHTML = '<p>尚未订阅任何主题</p>';
                return;
            }
            
            subscribedTopics.forEach(topic => {
                const topicItem = document.createElement('div');
                topicItem.className = 'topic-item';
                topicItem.innerHTML = `
                    <span>${topic}</span>
                    <div class="topic-actions">
                        <button class="danger" data-topic="${topic}">取消订阅</button>
                    </div>
                `;
                topicList.appendChild(topicItem);
            });
            
            // 添加取消订阅事件监听
            document.querySelectorAll('.topic-actions button').forEach(button => {
                button.addEventListener('click', function() {
                    const topic = this.getAttribute('data-topic');
                    unsubscribeTopic(topic);
                });
            });
        }
        
        // 连接MQTT服务器
        function connect() {
            const brokerUrl = brokerUrlInput.value.trim();
            if (!brokerUrl) {
                alert('请输入MQTT服务器地址');
                return;
            }
            
            let clientId = clientIdInput.value.trim();
            if (!clientId) {
                clientId = generateClientId();
                clientIdInput.value = clientId;
            }
            
            const options = {
                clientId: clientId,
                clean: true,
                reconnectPeriod: 1000,
                connectTimeout: 30 * 1000
            };
            
            // 添加认证信息（如果提供）
            const username = usernameInput.value.trim();
            const password = passwordInput.value.trim();
            if (username) {
                options.username = username;
            }
            if (password) {
                options.password = password;
            }
            
            // 更新状态为连接中
            statusIndicator.className = 'status-indicator status-connecting';
            statusText.textContent = '连接中...';
            
            try {
                // 使用MQTT.js连接
                client = mqtt.connect(brokerUrl, options);
                
                // 连接成功
                client.on('connect', () => {
                    updateConnectionStatus(true);
                    addSystemMessage(`成功连接到MQTT服务器: ${brokerUrl}`);
                });
                
                // 接收消息
                client.on('message', (topic, message) => {
                    addMessage(topic, message.toString(), 'received');
                });
                
                // 连接断开
                client.on('close', () => {
                    updateConnectionStatus(false);
                    addSystemMessage('连接已断开');
                });
                
                // 连接错误
                client.on('error', (error) => {
                    updateConnectionStatus(false);
                    addSystemMessage(`连接错误: ${error.message}`);
                });
                
            } catch (error) {
                updateConnectionStatus(false);
                addSystemMessage(`连接失败: ${error.message}`);
            }
        }
        
        // 断开连接
        function disconnect() {
            if (client) {
                client.end();
                client = null;
                updateConnectionStatus(false);
                addSystemMessage('已断开与MQTT服务器的连接');
            }
        }
        
        // 测试连接
        function testConnection() {
            addSystemMessage('正在测试连接...');
            const testClient = mqtt.connect(brokerUrlInput.value, {
                clientId: 'test_client_' + Math.random().toString(16).substr(2, 8),
                connectTimeout: 5000
            });
            
            testClient.on('connect', () => {
                addSystemMessage('测试连接成功！');
                testClient.end();
            });
            
            testClient.on('error', (error) => {
                addSystemMessage(`测试连接失败: ${error.message}`);
            });
        }
        
        // 订阅主题
        function subscribe() {
            const topic = subscribeTopicInput.value.trim();
            if (!topic) {
                alert('请输入要订阅的主题');
                return;
            }
            
            if (!client || !isConnected) {
                alert('请先连接到MQTT服务器');
                return;
            }
            
            client.subscribe(topic, (err) => {
                if (err) {
                    addSystemMessage(`订阅失败: ${err.message}`);
                } else {
                    subscribedTopics.add(topic);
                    updateTopicList();
                    addSystemMessage(`已成功订阅主题: ${topic}`);
                    subscribeTopicInput.value = '';
                }
            });
        }
        
        // 取消订阅
        function unsubscribeTopic(topic) {
            if (!client || !isConnected) {
                return;
            }
            
            client.unsubscribe(topic, (err) => {
                if (err) {
                    addSystemMessage(`取消订阅失败: ${err.message}`);
                } else {
                    subscribedTopics.delete(topic);
                    updateTopicList();
                    addSystemMessage(`已取消订阅主题: ${topic}`);
                }
            });
        }
        
        // 发布消息
        function publish() {
            const topic = publishTopicInput.value.trim();
            const message = messageInput.value.trim();
            
            if (!topic) {
                alert('请输入主题');
                return;
            }
            
            if (!message) {
                alert('请输入消息内容');
                return;
            }
            
            if (!client || !isConnected) {
                alert('请先连接到MQTT服务器');
                return;
            }
            
            client.publish(topic, message, (err) => {
                if (err) {
                    addSystemMessage(`发布失败: ${err.message}`);
                } else {
                    addMessage(topic, message, 'sent');
                    messageInput.value = '';
                }
            });
        }
        
        // 自动发布消息
        function toggleAutoPublish() {
            if (autoPublishInterval) {
                clearInterval(autoPublishInterval);
                autoPublishInterval = null;
                autoPublishBtn.textContent = '自动发布';
                addSystemMessage('已停止自动发布');
            } else {
                const topic = publishTopicInput.value.trim() || 'mqtt/demo';
                autoPublishInterval = setInterval(() => {
                    const message = `自动消息 #${Math.floor(Math.random() * 1000)} - ${new Date().toLocaleTimeString()}`;
                    client.publish(topic, message, (err) => {
                        if (!err) {
                            addMessage(topic, message, 'sent');
                        }
                    });
                }, 2000);
                autoPublishBtn.textContent = '停止自动发布';
                addSystemMessage('已开始自动发布，每2秒发送一条消息');
            }
        }
        
        // 清空消息日志
        function clearMessages() {
            messageContainer.innerHTML = '';
            messageCount = 0;
            messageCountElement.textContent = `消息: ${messageCount}`;
            addSystemMessage('日志已清空');
        }
        
        // 事件监听
        connectBtn.addEventListener('click', connect);
        disconnectBtn.addEventListener('click', disconnect);
        testBtn.addEventListener('click', testConnection);
        subscribeBtn.addEventListener('click', subscribe);
        publishBtn.addEventListener('click', publish);
        autoPublishBtn.addEventListener('click', toggleAutoPublish);
        clearBtn.addEventListener('click', clearMessages);
        
        // 服务器选项点击事件
        serverOptions.forEach(option => {
            option.addEventListener('click', function() {
                serverOptions.forEach(opt => opt.classList.remove('active'));
                this.classList.add('active');
                brokerUrlInput.value = this.getAttribute('data-url');
            });
        });
        
        // 初始化
        updateConnectionStatus(false);
        addSystemMessage('MQTT客户端已初始化，请输入连接信息并点击连接按钮');
        
        // 生成初始客户端ID
        clientIdInput.value = generateClientId();
    </script>
</body>
</html>